from flask import Flask
from flask import render_template, redirect, url_for, request, session, flash, Markup, abort
import os
import random, math
import datetime, time
# import importlib,sys
import hashlib
# importlib.reload(sys)
import requests, pprint, uuid

from utils import PermissionStr
from models import db, User, Host, Order
from wtfs import LoginForm, RegisterForm, HostForm
from decorators import login_required, permission_str_required

app = Flask(__name__, static_url_path='/', static_folder='static', template_folder='templates')
app.secret_key = 'idc by guojia'
basedir = os.path.abspath(os.path.dirname(__file__))
app.config.update(
    UPLOADED_PATH=os.path.join(basedir, 'uploads'),
)
# http://180.76.177.207:3312/api/?c=whm&a=add_vh&r=123456&s=19320f40f4d5768befd64f1552ffed5e&name=aaaa&passwd=aaaa&templete=php&subtemplete=php56&web_quota=222&db_quota=222&db_type=mysql&ftp=1&ftp_connect=0&ftp_usl=0&ftp_dsl=0&access=1&speed_limit=0&log_file=1&flow_limit=0&ignore_backup=0&domain=-1&subdir_flag=1&subdir=wwwroot&max_subdir=&module=php&htaccess=1&port=80,443s&edit=1&month=2&fmt=json

def getuuid8bit():
    array = [
        # "A", "B", "C", "D", "E", "F","G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S","T", "U", "V", "W", "X", "Y", "Z", 
        "0", "1", "2", "3", "4", "5","6", "7", "8", "9",
        "a", "b", "c", "d", "e", "f","g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s","t", "u", "v", "w", "x", "y", "z"]
    sid = str(uuid.uuid4()).replace("-",'')
    buffer = []
    for i in range(0, 8):
        start = i * 4
        end = i * 4 + 4
        v = int( sid[start : end], 16 )
        buffer.append(array[v % 36])
    # print("".join(buffer))
    return "".join(buffer)

def gen_add_vhost(host_name, host_pwd, size, key):
    a='add_vh'
    r=str(random.randint(1000, 9999))
    # r=str(123456)
    md5_str = hashlib.md5((a + key + r).encode('utf-8')).hexdigest()
    # print(f'md5_str {md5_str}')

    add_vhost_args = {
                    'c' : 'whm',
                    'a' : a,
                    'r' : r,
                    's' : md5_str,
                    'name' : host_name,
                    'passwd': host_pwd,
                    'templete': 'php',
                    'subtemplete': 'php56',
                    'web_quota' : size,
                    'db_quota' : size,
                    'db_type' : 'mysql',
                    'ftp' : 1,
                    'ftp_connect' : 0,
                    'ftp_usl' : 0,
                    'ftp_dsl' : 0,
                    'access' : 1,
                    'speed_limit' : 0,
                    'log_file' : 1,
                    'flow_limit' : 0,
                    'ignore_backup' : 0,
                    'domain' : -1,
                    'subdir_flag' : 1,
                    'subdir' : 'wwwroot',
                    'max_subdir' : '',
                    'module' : 'php',
                    'htaccess' : '1',
                    'port' : '80,443s',
                    # 'edit' : 0,
                    'month' : 1,
                    'fmt' : 'json',
                    'json' : 1
                }
    return add_vhost_args

def get_state_vhost(host_name, key):
    a='getVh'
    r=str(random.randint(1000, 9999))
    md5_str = hashlib.md5((a + key + r).encode('utf-8')).hexdigest()
    # print(f'md5_str {md5_str}')
    get_state_vhost_args = {
                    'c' : 'whm',
                    'a' : a,
                    'r' : r,
                    's' : md5_str,
                    'name' : host_name,
                    'fmt' : 'json',
                    'json' : 1,
                    't' : str(random.randint(1000, 9999))
                    }
    return get_state_vhost_args

def get_deposit_vhost(host_name):
    a='addExpireTime'
    get_deposit_vhost = {
                    'c' : 'product',
                    'a' : a,
                    'name' : host_name,
                    'month' : 1,
                    'fmt' : 'json',
                    'json' : 1
                    }
    return get_deposit_vhost

@app.route('/add/<string:username>/m/<string:money>')
def add_money(username, money):
    a_user = User.get_or_none(User.name == username)
    if a_user != None:
        urows = (User.update({User.balance: User.balance + round(int(money)) }).where(User.name == a_user.name).execute())
        # print(f'urows : { urows }')
    return redirect(url_for('index'))

@app.route('/change/host/pwd/<string:host_name>/<string:host_pwd>')
@app.route('/change/host/pwd/<string:host_name>/<string:host_pwd>/<string:username>/<int:page>', methods=['GET', 'POST'])
@app.route('/change/host/pwd/<string:host_name>/<string:host_pwd>/<string:username>/<int:page>/<string:key>', methods=['GET', 'POST'])
def change_password_vhost(host_name, host_pwd, username, page, key):
    a='change_password'
    r=str(random.randint(1000, 9999))
    md5_str = hashlib.md5((a + key + r).encode('utf-8')).hexdigest()
    # print(f'md5_str {md5_str}')
    change_vhost_password_args = {
                    'c' : 'whm',
                    'a' : a,
                    'r' : r,
                    's' : md5_str,
                    'name' : host_name,
                    'passwd': host_pwd,
                    'fmt' : 'json',
                    'json' : 1
                    }

    a_order = Order.get_or_none(Order.host_name == host_name)
    url = f'http://{a_order.hoster.ip}:3312/api/'
    if a_order != None:
        urows = (Order.update({Order.host_pwd: host_pwd }).where(Order.host_name == host_name).execute())
        ret = requests.get(url, params=change_vhost_password_args)
        if urows and 200 == ret.json()["result"]:
            # abort(404)
            return redirect(f'/order/{username}/p/{page}')
        else:
            abort(404)

@app.route('/del/host/<string:host_name>/<string:username>/<int:page>/<string:key>', methods=['GET', 'POST'])
def delete_vhost(host_name, username, page, key):
    a='del_vh'
    r=str(random.randint(1000, 9999))
    md5_str = hashlib.md5((a + key + r).encode('utf-8')).hexdigest()
    # print(f'md5_str {md5_str}')
    del_vhost_args = {
                    'c' : 'whm',
                    'a' : a,
                    'r' : r,
                    's' : md5_str,
                    'name' : host_name,
                    'fmt' : 'json',
                    'json' : 1
                    }

    a_order = Order.get_or_none(Order.host_name == host_name)
    url = f'http://{a_order.hoster.ip}:3312/api/'
    if a_order != None:
        drows = Order.delete().where(Order.host_name == host_name).execute()
        # print(f'delete Order  : {drows}')
        ret = requests.get(url, params=del_vhost_args)
        if drows and 200 == ret.json()["result"]:
            # abort(404)
            return redirect(f'/order/{username}/p/{page}')
        else:
            abort(404)

@app.route('/', methods=['GET', 'POST'])
def index():
    tips = '首页'

    hosts = Host.select().execute()
    
    # print(f'hosts { list(hosts) }')

    if request.method == 'POST':
        # print(f'cross post and validate()....')
        host_range = request.form.get('range')
        # print(f' post host_range : {host_range}')
        ip = request.form.get('hostip')
        # print(f'ip : {ip}')

        if int(host_range) < 100 or int(host_range) > 1000:
            tips = '😭最少100M最大1G'
        else:
            if not session.get('isLogin'):
                tips = '😭请先登陆'
            else:
                a_user = User.get_or_none(User.name == session.get('username'))
                if a_user != None:
                    rmb = int(a_user.balance) - int(int(host_range) / 100)
                    if rmb < 0:
                        tips = '😭余额不足'
                    else:
                        host = Host.get_or_none(ip=ip)
                        if None == host:
                            tips = '😭主机不存在'
                        else:
                            uuid8bit = getuuid8bit()
                            host_counts = Order.select().count()
                            gen_hostname = f'h{(host_counts+1)}{uuid8bit}'
                            # print(f'gen_hostname : {gen_hostname}')
                            # print(f'gen_hostpassword : {uuid8bit}')
                            # print(f'host security key : { host.security_code }')
                            # print(f'host ip : { host.ip }')

                            create_host_name = gen_hostname
                            create_host_pwd = uuid8bit
                            # create_host_pwd = '1a2b3c4d5f6'
                            create_size = host_range
                            # print( gen_add_vhost(host_name, host_pwd, size, host.security_code) )
                            add_vhost_args = gen_add_vhost(create_host_name, create_host_pwd, create_size, host.security_code)
                            
                            url = f'http://{host.ip}:3312/api/'
                            ret = requests.get(url, params=add_vhost_args)
                            
                            # print(f' ret.json() { ret.json() } ')
                            # print(f' ret.json() { ret.json()["result"] } ')
                            # print(f' ret.status_code { ret.status_code } ')
                            # print(f'ret : {ret.url}')
                            # print(f'ret : {ret.text}')
                            # print(f'ret : {ret.encoding}')
                            # print(f'ret : {ret.content}')
                            # print(f'ret : {ret.json()}')

                            if 200 == ret.json()["result"]:
                                # print(f'ret : {ret.url}')
                                # print(f'ret : {ret.text}')
                                # print(f'ret : {ret.encoding}')
                                # print(f'ret : {ret.content}')
                                # print(f'ret : {ret.json()}')
                                
                                # a_user = User.get_or_none(User.name == session.get('username'))
                                a_host = Host.get_or_none(Host.ip == host.ip)
                                # if a_user != None and a_host != None:
                                if a_host != None:
                                    # print(f'create_host_name : { create_host_name }')
                                    # print(f'create_host_pwd : { create_host_pwd }')
                                    # print(f'host security key : { host.security_code }')
                                    # print(f'host create_size : { create_size }')
                                    # print(f'host ip : { host.ip }')
                                    o_id = Order.insert(owner = a_user,
                                                            hoster = a_host,
                                                            host_name = create_host_name,
                                                            host_pwd = create_host_pwd,
                                                            host_size = create_size,
                                                            db_size = create_size,
                                                            create_time = datetime.datetime.now(),
                                                            update_time = datetime.datetime.now() ).execute()
                                    # print(f'order id insert: {o_id}')
                                    
                                    urows = (User.update({User.balance: rmb })
                                                .where(User.name == a_user.name).execute())
                                    if o_id and urows:
                                        # print(f' session balance : {session.get("userbalance")}')
                                        session['userbalance'] = rmb
                                        # print(f' update session balance : {session.get("userbalance")}')
                                        tips = '🥰主机开通成功'
                            else:
                                tips = f'😭主机开通失败 {ret.json()["result"]}'
                                

    data = {"hosts": hosts, "tips": tips, "isLogin": session.get('isLogin'), "username": session.get('username'), "userbalance": session.get('userbalance')}
    return render_template('index.html', **data)

@app.route("/order/<string:username>", methods=['GET'])
@app.route("/order/<string:username>/p/<int:page>", methods=['GET','POST'])
@app.route("/order/deposit/<string:host_name>/<string:username>/<int:page>", methods=['GET', 'POST'])
@login_required
@permission_str_required(PermissionStr.READ)
def order(host_name=None, username=None, key=None, page = 1):
    per_page = 3
    tips = '用户订单'

    if request.method == 'POST':
        a_user = User.get_or_none(User.name == session.get('username'))
        a_order = Order.get_or_none(Order.host_name == host_name)
        if a_user != None and a_order != None:
            rmb = int(a_user.balance)
            order_price = int(int(a_order.host_size) / 100)
            calc = rmb - order_price
            if  calc < 0:
                tips = '😭余额不足'
            else:
                url = f'http://{a_order.hoster.ip}:3312/admin/index.php/'
                get_deposit_vhost_args = get_deposit_vhost(host_name)
                try:
                    ret = requests.get(url, params=get_deposit_vhost_args, timeout=(3,9))
                    if 200 == ret.json()["result"]:
                        a_user.balance = calc
                        urows = (User.update({User.balance: a_user.balance })
                                        .where(User.name == a_user.name).execute())
                        if urows:
                            # print(f' session balance : {session.get("userbalance")}')
                            session['userbalance'] = a_user.balance
                            # print(f' update session balance : {session.get("userbalance")}')
                            tips = '🥰成功续费一个月'
                            tips = '🥰' + ret.json()["msg"]
                    else:
                        tips = ret.json()["msg"]
                except:
                    tips = '😭服务器网络请求异常'
        else:
            tips = '😭用户订单不存在'


    # orders = Order.select()
    
    totals = Order.select().join(User, on=(Order.user_id == User.id)).where(User.name == username).count()
    total_pages = math.ceil(totals / per_page)
    # print(f'total_pages : { total_pages }')

    orders = Order.select().join(User, on=(Order.user_id == User.id)).where(
        User.name == username).paginate(page, per_page)

    # print(f'oooooooooooorders obj : { orders.count() }')
    # print(f' orders { orders }........')
    Order.state = None
    for order in orders:
        get_state_vhost_args = get_state_vhost(order.host_name, order.hoster.security_code)
        url = f'http://{order.hoster.ip}:3312/api/'
        try:
            ret = requests.get(url, params=get_state_vhost_args, timeout=(3,9))
            # print(f'ffffffffffffffffff order {ret.json()["result"]}')
            if 200 == ret.json()["result"]:
                order.state = ret.json()["status"]
                # print(f'order.state {order.state}')
                start = round(float(ret.json()["create_time"]) )
                end = round(float(ret.json()["expire_time2"]))
                order.start = time.strftime("%Y/%m/%d %H:%M:%Ss", time.localtime(start))
                if end:
                    order.end = time.strftime("%Y/%m/%d %H:%M:%Ss", time.localtime(end))
                else:
                    order.end = 0
            else:
                order.state = False
                continue
        except:
            order.state = False
            continue
        # print(f'ret json {ret.json()}')


    data = { "tips": tips, "page" : page, "total_pages" : total_pages, "orders": orders, "isLogin": session.get('isLogin'), "username": session.get('username'), "userbalance": session.get('userbalance')}
    return render_template('order.html', **data)

@app.route('/host/del/<int:hid>', methods=['GET', 'POST'])
@login_required
@permission_str_required(PermissionStr.ROOT)
def host_del_by_id(hid):
    host_tips = '删除主机节点'
    host = Host.get_or_none(id=hid)
    if host != None:
        Host.delete_instance(host, recursive=True)
        host_tips = '🥰删除主机节点删除成功'
    else:
        host_tips = '😭删除主机节点删除失败'
    return redirect(url_for('host'))

@app.route('/host', methods=['GET', 'POST'])
@login_required
@permission_str_required(PermissionStr.ROOT)
def host():
    tips = '添加主机节点'

    form = HostForm()

    ip = form.ip.data
    area = form.area.data
    security_code = form.security_code.data
    server_account = form.server_account.data
    server_password = form.server_password.data
    # print(f' ip : {ip}')
    # print(f' area : {area}')
    # print(f' security_code : {security_code}')
    # print(f' server_account : {server_account}')
    # print(f' server_password : {server_password}')

    if request.method == 'POST' and form.validate():
        # print(f'cross post and validate()....')
        with db.atomic():
            host_id = Host.insert(ip=ip, 
                                  area=area,
                                  security_code=security_code,
                                  server_account=server_account,
                                  server_password=server_password).execute()
        
        host = Host.get_or_none(ip=ip)
        if None == host:
            tips = '😭主机节点添加失败'
        else:
            tips = '🥰主机节点添加成功'
    else:
        pass

    hosts = Host.select().execute()
    # print(f'hosts {hosts}')

    data = {"hosts": hosts, "form": form, "tips": tips, "isLogin": session.get('isLogin'), "username": session.get('username'), "userbalance": session.get('userbalance')}
    return render_template('host.html', **data)

@app.route('/login', methods=['GET', 'POST'])
def login():
    tips = '登陆'
    form = LoginForm()

    if request.method == 'POST' and form.validate():
        name = form.name.data
        password = form.password.data
        token = request.form.get('token')
        # token = '11111111111111111111'
        # print(f'token : { token } { type(token) } { len(token) }')

        user = User.get_or_none(name=name)
        if None == user.pwd or user.pwd != password:
            form.password.errors.insert(0, '🙃密码错误')
        else:
            if len(token) < 6:
                tips = '😭验证码未通过验证'
            else:
                session['isLogin'] = True
                session['username'] = name
                session['userbalance'] = user.balance
                
                now = datetime.datetime.now()
                u_user = User.update(last_login = now, login_count = User.login_count + 1).where(User.name == name).execute()
                # rowid = (User
                #         # .insert(name='ccc', pwd='ccc', role='admin')
                #         .insert(user.asdict())
                #         .on_conflict(
                #             conflict_target=[User.id],  # Which constraint?
                #             # preserve=[User.pwd, User.role],  # Use the value we would have inserted.
                #             preserve=[],  # Use the value we would have inserted.
                #             update={User.last_login: now, User.login_count: User.login_count + 1})
                #             .execute()
                #         )
                # print(f'rowid : {rowid} .')

                return redirect(url_for('index'))
    else:
        pass

    data = { "tips": tips, "form": form, "isLogin": session.get('isLogin'), "username": session.get('username'), "userbalance": session.get('userbalance')}
    return render_template('login.html', **data)

@app.route('/register', methods=['GET', 'POST'])
def register():
    tips = '注册'
    form = RegisterForm()
    name = form.name.data
    password = form.password.data
    # confirm = form.confirm.data
    # token = request.form.get('token')
    # print(f'name : { name }')
    # print(f'password : { password }')
    # print(f'confirm : { confirm }')

    if request.method == 'POST' and form.validate():
        token = request.form.get('token')
        if len(token) < 6:
            tips = '😭验证码未通过验证'
        else:
            with db.atomic():
                user_id = User.insert(name= name, pwd=password, role='user').execute()
                user = User.get_or_none(id=user_id)
                if None == user:
                    tips = '😭注册失败'
                else:
                    tips = Markup('🥰注册成功<a href="/login">点我登陆</a>')
    else:
        pass
    
    data = { "tips": tips, "form": form, "isLogin": session.get('isLogin'), "username": session.get('username'), "userbalance": session.get('userbalance')}
    return render_template('register.html', **data)

@app.route('/logout')
def logout():
    session['username'] = None
    session['userbalance'] = None
    session['isLogin'] = False
    return redirect('/')

@app.errorhandler(404)
def page_not_found(e):
    return render_template('404.html'), 404

@app.errorhandler(500)
def page_not_found(e):
    return render_template('404.html'), 500

@app.route('/err')
def server_error():
    if session.get('isLogin'):
        return render_template('404.html')
    else:
        abort(500)

@app.cli.command()
def init():
    db.drop_tables([User, Host, Order], safe=True)
    db.create_tables([User, Host, Order], safe=True)

    User.create_table(safe=True)
    Host.create_table(safe=True)
    Order.create_table(safe=True)

    user_datas = [
        {'name': 'jia','pwd': 'jia', 'role': 'user','balance':'123.0', 'last_login': datetime.datetime.now()},
    ]
    user_insert_sql = User.insert_many(user_datas).execute()
    a_user = User.get_or_none(User.name == "jia")
    a_user.auths = a_user.auths + ":root"
    a_user.save()
    # print(f'a_user : { a_user }')
    u_user = User.update(auths = User.auths + ":admin").where(User.name == "jia").execute()
    # print(f'u_user : { u_user }')
    
    host_datas = [
        {'ip' : '180.76.177.207', 'area' : '北京', 'security_code' : 'EuDmKWHRSn5XmcTc', 'server_account':'root', 'server_password':'admin'},
        {'ip' : '164.155.99.88', 'area' : '香港', 'security_code' : 'G7Dq9Jp8KhhQfTqQ', 'server_account':'root', 'server_password':'admin'},
      ]
    host_insert_sql = Host.insert_many(host_datas).execute()
    
    # a_user = User.get_or_none(User.name == "jia")
    # a_host = Host.get_or_none(Host.area == '北京')
    # if a_user != None and a_host != None:
    #     # for i in range(7):
    #     o_id = Order.insert(owner = a_user,
    #                             hoster = a_host,
    #                             host_name = 'jia',
    #                             host_pwd = 'jia',
    #                             host_size = 123,
    #                             db_size = 123,
    #                             create_time = datetime.datetime.now(),
    #                             update_time = datetime.datetime.now() ).execute()
    # print(f'order id insert: {o_id}')

    # a_user = User.get_or_none(User.name == "jia")
    # a_host = Host.get_or_none(Host.area == '北京')
    # if a_user != None and a_host != None:
    #     # for i in range(7):
    #     o_id = Order.insert(owner = a_user,
    #                             hoster = a_host,
    #                             host_name = 'jia',
    #                             host_pwd = 'jia',
    #                             host_size = 111,
    #                             db_size = 111,
    #                             create_time = datetime.datetime.now(),
    #                             update_time = datetime.datetime.now() ).execute()
    # print(f'order id insert: {o_id}')

@app.cli.command()
def query():
    page = 1
    per_page = 3
    a_user = User.get_or_none(User.name == 'jia')
    a_host = Host.get_or_none(Host.area == '北京')
    orders = Order.select().join(User, on=(Order.user_id == User.id)).where(User.name == 'abc').paginate(page, per_page)
    # orders = Order.select().paginate(page, per_page)
    for order in orders:
        print(f'order paginate { order }')
    

@app.cli.command()
def order():
    try:
        a_user = User.get_or_none(User.name == "jia")
        a_host = Host.get_or_none(Host.area == '北京')
        if a_user != None and a_host != None:
            # for i in range(10):
            o_id = Order.insert(owner=a_user,
                                hoster=a_host,
                                # host_ip=a_host.ip, 
                                host_name='abc',
                                host_pwd='abc',
                                host_size=111,
                                db_size=111,
                                # host_area=a_host.area,
                                # login_url=a_host.login_url,
                                create_time=datetime.datetime.now(),
                                update_time=datetime.datetime.now() ).execute()
            print(f'order id insert: {o_id}')
    except User.DoesNotExist:
        a_user = None
        a_host = None
        print(f'a_user : {a_user}')
        print(f'a_host : {a_host}')